package com.zt.proxy.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.validation.constraints.NotNull;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.alibaba.dubbo.rpc.service.GenericService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.zt.proxy.cache.MethodCaches;
import com.zt.proxy.cache.ZkClientCaches;
import com.zt.proxy.dto.ConnectDTO;
import com.zt.proxy.dto.ResultDTO;
import com.zt.proxy.dto.UrlModelDTO;
import com.zt.proxy.exception.ServiceException;
import com.zt.proxy.handler.DubboExecuteHandler;
import com.zt.proxy.handler.ZkClientHandler;
import com.zt.proxy.model.ApplicationModel;
import com.zt.proxy.model.MethodModel;
import com.zt.proxy.model.UrlModel;
import com.zt.proxy.service.ConnectService;
import com.zt.proxy.util.ParamUtil;
import com.zt.proxy.util.StringUtil;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class ConnectServiceImpl implements ConnectService{
    
    private final DubboExecuteHandler handler = new DubboExecuteHandler();

    @Value("${proxy.filter.serviceNames}")
    private String serviceNames;
    
    @Override
    public List<ApplicationModel> connect(@NotNull String conn) throws NoSuchFieldException, IllegalAccessException {
        // get client
        ZkClientHandler client = ZkClientCaches.getHandler(conn);
        
        if (!client.isAvailable()) {
            throw new ServiceException(StringUtil.format("can't connect to {}", conn));
        }
        
        log.info("filter.serviceNames:{}", serviceNames);

        return client.getInterfaces(Arrays.asList(serviceNames.split(";")));
        
    }

    @Override
    public List<UrlModelDTO> listProviders(@NotNull ConnectDTO connect) throws NoSuchFieldException,
                                                                        IllegalAccessException {
     // get client
        ZkClientHandler client = ZkClientCaches.getHandler(connect.getConn());

        if (null == client) {
            throw new ServiceException("the cache is validate, please reconnect to zk againt.");
        }

        List<UrlModel> providers = client.getProviders(connect);

        List<UrlModelDTO> ret = new ArrayList<>();
        providers.forEach(p -> {
            UrlModelDTO m = new UrlModelDTO();
            m.setKey(p.getKey());
            m.setHost(p.getUrl().getHost());
            m.setPort(p.getUrl().getPort());

            ret.add(m);
        });

        return ret;
    }

    @Override
    public ResultDTO<String> send(ConnectDTO dto) throws Exception {

        GenericService genericService = this.handler.buildGenericService(dto);
        
        // get method
        MethodModel methodModel = MethodCaches.get(dto.getMethodKey());
        // parse parameter
        Object[] params = ParamUtil.parseJson(dto.getJson(), methodModel.getMethod());
        String[] parameterTypes = ParamUtil.getparamTypes(methodModel.getMethod());
        
        Object result = genericService.$invoke(methodModel.getMethod().getName(), parameterTypes, params);
        return ResultDTO.createSuccessResult("SUCCESS",
                                             JSON.toJSONString(result, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat),
                                             String.class);      
    }

}
